package com.company;
//https://www.cnblogs.com/skywang12345/p/3479224.html

public class C07MultiThreadingWaitNotify {
    /*wait(), notify(), notifyAll()等方法介绍
在Object.java中，定义了wait(), notify()和notifyAll()等接口。wait()的作用是让当前线程进入等待状态，同时，
wait()也会让当前线程释放它所持有的锁。而notify()和notifyAll()的作用，
则是唤醒当前对象上的等待线程；notify()是唤醒单个线程，而notifyAll()是唤醒所有的线程。

Object类中关于等待/唤醒的API详细信息如下：
notify()        -- 唤醒在此对象监视器上等待的单个线程。
notifyAll()   -- 唤醒在此对象监视器上等待的所有线程。
wait()                                         -- 让当前线程处于“等待(阻塞)状态”，
“直到其他线程调用此对象的 notify() 方法或 notifyAll() 方法”，当前线程被唤醒(进入“就绪状态”)。
wait(long timeout)                    -- 让当前线程处于“等待(阻塞)状态”，“直到其他线程调用此对象的 notify()
方法或 notifyAll() 方法，或者超过指定的时间量”，当前线程被唤醒(进入“就绪状态”)。
wait(long timeout, int nanos)  -- 让当前线程处于“等待(阻塞)状态”，“直到其他线程调用此对象的 notify() 方法或 notifyAll() 方法，
或者其他某个线程中断当前线程，或者已超过某个实际时间量”，当前线程被唤醒(进入“就绪状态”)。

 */
//    2. wait()和notify()示例

    public static void main(String[] args) {
//        新建“线程t1”
        ThreadA t1 = new ThreadA("t1");

//        获取“t1对象的同步锁”
        synchronized (t1) {
            try {
                // 启动“线程t1”
                System.out.println(Thread.currentThread().getName() + " start t1");
                t1.start();

                System.out.println(Thread.currentThread().getName() + " wait()");
                //“主线程”执行t1.wait() 释放“t1对象的锁”并且进入“等待(阻塞)状态
                // 主线程等待t1通过notify()唤醒。
//                虽然t1.wait()是通过“线程t1”调用的wait()方法，但是调用t1.wait()的地方是在“主线程main”中。而主线程必须是“当前线程”，也就是运行状态，
//                才可以执行t1.wait()。所以，此时的“当前线程”是“主线程main”！因此，t1.wait()是让“主线程”等待，而不是“线程t1”！
//                t1.notify(); 无效。因为t1实例锁并没有释放。
                t1.wait();


                System.out.println(Thread.currentThread().getName() + " continue");
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
    }

    //    (01) 注意，图中"主线程" 代表“主线程main”。"线程t1" 代表WaitTest中启动的“线程t1”。 而“锁” 代表“t1这个对象的同步锁”。
//            (02) “主线程”通过 new ThreadA("t1") 新建“线程t1”。随后通过synchronized(t1)获取“t1对象的同步锁”。然后调用t1.start()启动“线程t1”。
//            (03) “主线程”执行t1.wait() 释放“t1对象的锁”并且进入“等待(阻塞)状态”。等待t1对象上的线程通过notify() 或 notifyAll()将其唤醒。
//            (04) “线程t1”运行之后，通过synchronized(this)获取“当前对象的锁”；接着调用notify()唤醒“当前对象上的等待线程”，也就是唤醒“主线程”。
//            (05) “线程t1”运行完毕之后，释放“当前对象的锁”。紧接着，“主线程”获取“t1对象的锁”，然后接着运行。
    static class ThreadA extends Thread {

        public ThreadA(String name) {
            super(name);
        }

        public void run() {
            synchronized (this) { // maint函数⇒t1.wait()执行后，才会执行下面的代码。（t1.wait()执行后才释放t1的实例锁，同时主线程进入阻塞状态）

                System.out.println(Thread.currentThread().getName() + " call notify()");
                try {
                    currentThread().sleep(5000);
                    // 唤醒当前的wait线程，wait线程：本例中是主线程
                    notify();
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }

            }
        }
    }

//    3. wait(long timeout)和notify()
/*wait(long timeout)会让当前线程处于“等待(阻塞)状态”，“直到其他线程调用此对象的 notify() 方法或 notifyAll() 方法，
    或者超过指定的时间量”，当前线程被唤醒(进入“就绪状态”)。
    下面的示例就是演示wait(long timeout)在超时情况下，线程被唤醒的情况。*/

//    public static void main(String[] args) throws InterruptedException {
//
//        System.out.println("mainThread is running");
//        MyThread myThread =new MyThread();
//        synchronized (myThread){
//            System.out.println("当前线程：主线程（注意不是子线程）阻塞并且等待，释放实例对象myThread锁 ");
//            myThread.wait(3000);
//            System.out.println("主线程继续");
//        }
//
//    }
//
//   public static class MyThread extends Thread{
//       @Override
//       public void run(){
//           synchronized(this){
//               System.out.println(this.getName()+"run");
//               while (true){
//                   ;
//               }
//           }
//       }
//   }
/*4. wait() 和 notifyAll()
    通过前面的示例，我们知道 notify() 可以唤醒在此对象监视器上等待的单个线程。
    下面，我们通过示例演示notifyAll()的用法；它的作用是唤醒在此对象监视器上等待的所有线程。*/

/*(01) 主线程中新建并且启动了3个线程"t1", "t2"和"t3"。
(02) 主线程通过sleep(3000)休眠3秒。在主线程休眠3秒的过程中，
    我们假设"t1", "t2"和"t3"这3个线程都运行了。
    以"t1"为例，当它运行的时候，它会执行obj.wait()等待其它线程通过notify()或额nofityAll()来唤醒它；
    相同的道理，"t2"和"t3"也会等待其它线程通过nofity()或nofityAll()来唤醒它们。
(03) 主线程休眠3秒之后，接着运行。执行 obj.notifyAll() 唤醒obj上的等待线程，
    即唤醒"t1", "t2"和"t3"这3个线程。 紧接着，主线程的synchronized(obj)运行完毕之后，
    主线程释放“obj锁”。这样，"t1", "t2"和"t3"就可以获取“obj锁”而继续运行了！*/

//    public static Object obj=new Object();
//    public static class MyThread extends Thread{
//        private long mils;
//        public MyThread(long mils){
//            this.mils=mils;
//        }
//
//       @Override
//       public void run(){
//           try {
//               // 同步锁
//               synchronized (obj){
//                   System.out.println(Thread.currentThread().getName()+"获得同步锁！ 等待5秒后wait");
//                   try {
//                       Thread.currentThread().sleep(5000);
//                   } catch (InterruptedException e) {
//                       e.printStackTrace();
//                   }
//
//                   obj.wait();
////                   currentThread().sleep(this.mils);
//
//                   System.out.println(getName()+"正在结束！5秒后结束！");
//                   try {
//                       Thread.currentThread().sleep(5000);
//                   } catch (InterruptedException e) {
//                       e.printStackTrace();
//                   }
//                   obj.notifyAll();
//               }
//
//           } catch (InterruptedException e) {
//               e.printStackTrace();
//           }
//
//       }
//
//    }
//
//    public static void main(String[] arg)  {
//
//        MyThread th1=new MyThread(1000);
//        MyThread th2=new MyThread(2000);
//        MyThread th3=new MyThread(2500);
//        MyThread th4=new MyThread(3000);
//
//        th1.start();
//        th2.start();
//        th3.start();
//        th4.start();
//        try {
//            Thread.currentThread().sleep(3000);
//        } catch (InterruptedException e) {
//            e.printStackTrace();
//        }
//        synchronized (obj){
//            System.out.println(Thread.currentThread().getName()+"唤醒在此对象obj监视器上等待的所有线程！");;
//            obj.notifyAll();
//            try {
//                Thread.currentThread().sleep(5000);
//            } catch (InterruptedException e) {
//                e.printStackTrace();
//            }
//            System.out.println(Thread.currentThread().getName()+"运行继续。。。！");
//        }
//    }

}
